/* Turska Framework/UI library (Turs2)
 * Copyright 2003-2008 Jetro Lauha
 * All rights reserved.
 * Web: http://iki.fi/jetro/turska/
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of EITHER:
 *   (1) The GNU Lesser General Public License as published by the Free
 *       Software Foundation; either version 2.1 of the License, or (at
 *       your option) any later version. The text of the GNU Lesser
 *       General Public License is included with this library in the
 *       file LICENSE-LGPL.txt.
 *   (2) The BSD-style license that is included with this library in
 *       the file LICENSE-BSD.txt.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
 * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details.
 *
 * $Id: main.cpp 174 2010-12-14 13:57:46Z jlauha@gmail.com $
 * $Revision: 174 $
 */

#ifdef _MSC_VER
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif // _MSC_VER

#include <assert.h>

#include "main.h"


static SDL_Surface *sScreen;


bool gAlive = true;

int gVersion = 0x000100;
char *gVersionString = "0.1.0";


static SDL_Surface * sSetVideoMode(int width, int height, int bpp, Uint32 videoFlags, int fsaa)
{
    SDL_Surface *result;
    int rSize, gSize, bSize;

    switch (bpp)
    {
    case 8:
        rSize = 3;
        gSize = 3;
        bSize = 2;
        break;
    case 15:
    case 16:
        rSize = 5;
        gSize = 5;
        bSize = 5;
        break;
    default:
        rSize = 8;
        gSize = 8;
        bSize = 8;
        break;
    }
    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, rSize);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, gSize);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, bSize);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    if (fsaa)
    {
        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, fsaa);
    }

    result = SDL_SetVideoMode(width, height, bpp, videoFlags);


    if (result != NULL)
    {
        int noAcceleration = 0, acceleratedVisual;

        // Check if OpenGL reports that acceleration isn't available.
        // (uses WGL_ACCELERATION_ARB on WIN32)
        if (SDL_GL_GetAttribute(SDL_GL_ACCELERATED_VISUAL, &acceleratedVisual) == 0)
        {
            if (!acceleratedVisual)
                noAcceleration = 1;
        }
        else
        {
            // SDL_GL_ACCELERATED_VISUAL wasn't available, check if we can detect
            // software OpenGL implementations from GL_VENDOR and GL_RENDERER
            // strings. (what about GL_VERSION?).
            const char *glVendor = (const char *)glGetString(GL_VENDOR);
            const char *glRenderer = (const char *)glGetString(GL_RENDERER);
            //const char *glVersion = (const char *)glGetString(GL_VERSION);
            if (glVendor == NULL || glRenderer == NULL)
                noAcceleration = 1;
            else
            {
                if (strstr(glVendor, "Microsoft") != NULL &&
                    strstr(glRenderer, "GDI") != NULL)
                {
                    // Seems we're running with the MS software renderer, example:
                    // Vendor: "Microsoft Corporation"
                    // Renderer: "GDI Generic"
                    // - Could maybe check also that glVersion is "1.1.0"
                    noAcceleration = 1;
                }
                else if (strstr(glVendor, "Apple") != NULL &&
                         strstr(glRenderer, "Apple Software Renderer") != NULL)
                {
                    // Seems we're running with the Apple SW renderer, example:
                    // Vendor: "Apple Computer, Inc."
                    // Renderer: "Apple Software Renderer"
                    noAcceleration = 1;
                }
                else if (strstr(glVendor, "Mesa") != NULL &&
                         strstr(glRenderer, "Mesa") != NULL)
                {
                    // Seems we're running with Mesa software renderer, example:
                    // Vendor: "Mesa project: www.mesa3d.org"
                    // Renderer: "Mesa GLX Indirect"
                    // NOTE: It seems to be common for (Linux) drivers to have
                    //       "Mesa" in renderer string but something else as
                    //       the vendor. (In which case the driver is probably
                    //       hardware accelerated.)
                    noAcceleration = 1;
                }
            }
        }

        if (noAcceleration)
        {
            result = NULL;
            SDL_QuitSubSystem(SDL_INIT_VIDEO);
            SDL_InitSubSystem(SDL_INIT_VIDEO);
        }

    }

    return result;
}


static BOOL sCheckVideoMode(int width, int height, int bpp, int &oFlags, BOOL forceWindowed, BOOL forceFullscreen)
{
    SDL_Rect **modes;
    SDL_PixelFormat format;
    int f, flagsCount = 1, flags[2];

    if (forceFullscreen)
        flags[0] = SDL_OPENGL | SDL_FULLSCREEN;
    else if (forceWindowed)
        flags[0] = SDL_OPENGL;
    else
    {
        flags[0] = SDL_OPENGL;
        flags[1] = SDL_OPENGL | SDL_FULLSCREEN;
        flagsCount = 2;
    }

    for (f = 0; f < flagsCount; ++f)
    {
        oFlags = flags[f];

        format.BitsPerPixel = bpp;
        modes = SDL_ListModes(&format, oFlags);
        if (modes == (SDL_Rect **)NULL)
            return FALSE;
        else if (modes == (SDL_Rect **)-1)
            return TRUE;
        else
        {
            int m;
            for (m = 0; modes[m]; ++m)
            {
                if (modes[m]->w == width && modes[m]->h == height)
                    return TRUE;
            }
        }
    }

    return FALSE;
}


static SDL_Surface * sInitScreenMode(int width, int height, bool forceWindowed, bool forceFullscreen)
{
    SDL_Surface *screen = NULL;
    BOOL modeFound = FALSE;
    int bpp;
    int flags;
    const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo();
    if (videoInfo && videoInfo->vfmt->BitsPerPixel > 8)
    {
        bpp = videoInfo->vfmt->BitsPerPixel;
        modeFound = sCheckVideoMode(width, height, bpp, flags, forceWindowed, forceFullscreen);
    }
    if (!modeFound)
    {
        bpp = 32;
        modeFound = sCheckVideoMode(width, height, bpp, flags, forceWindowed, forceFullscreen);
        if (!modeFound)
        {
            bpp = 16;
            modeFound = sCheckVideoMode(width, height, bpp, flags, forceWindowed, forceFullscreen);
        }
        if (!modeFound)
        {
            bpp = 15;
            modeFound = sCheckVideoMode(width, height, bpp, flags, forceWindowed, forceFullscreen);
        }
        if (!modeFound)
        {
            bpp = 24;
            modeFound = sCheckVideoMode(width, height, bpp, flags, forceWindowed, forceFullscreen);
        }
    }

    int tries;

    for (tries = 0; tries < 2 && screen == NULL; ++tries)
    {
        if (bpp == 15 || bpp == 16)
        {
            screen = sSetVideoMode(width, height, bpp, flags, 0);
            if (screen == NULL)
                screen = sSetVideoMode(width, height, 32, flags, 0);
            if (screen == NULL)
                screen = sSetVideoMode(width, height, 24, flags, 0);
        }
        else if (bpp == 24)
        {
            screen = sSetVideoMode(width, height, bpp, flags, 0);
            if (screen == NULL)
                screen = sSetVideoMode(width, height, 32, flags, 0);
            if (screen == NULL)
                screen = sSetVideoMode(width, height, 16, flags, 0);
        }
        else
        {
            // initial bpp is 32
            screen = sSetVideoMode(width, height, bpp, flags, 0);
            if (screen == NULL)
                screen = sSetVideoMode(width, height, 16, flags, 0);
            if (screen == NULL)
                screen = sSetVideoMode(width, height, 24, flags, 0);
        }

        if (screen == NULL)
            flags ^= SDL_FULLSCREEN;
    }

    return screen; // NULL if unsuccessful
}


// Note: Link with SDL_main library.
int main(int argc, char* argv[])
{
    bool forceWindowed = false;
    bool forceFullscreen = false;

    putenv("SDL_VIDEO_WINDOW_POS=center");
    putenv("SDL_VIDEO_CENTERED=1");

    if (argc > 1 && argv[1][0] == 'w')
        forceWindowed = TRUE;
    if (argc > 1 && argv[1][0] == 'f')
        forceFullscreen = TRUE;

    if (SDL_Init(/*SDL_INIT_AUDIO | */SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) < 0)
    {
        gErrorMessage("Unable to init SDL", SDL_GetError());
        exit(EXIT_FAILURE);
    }
    atexit(SDL_Quit);

    sScreen = sInitScreenMode(640, 480, forceWindowed, forceFullscreen);

    if (sScreen == NULL)
    {
        gErrorMessage("Unable to initialize video mode", SDL_GetError());
        exit(EXIT_FAILURE);
    }

    SDL_WM_SetCaption("Loading...", "");
    SDL_EnableKeyRepeat(250, 60);
    SDL_EnableUNICODE(1);

    // Display possible splash screen here and then load data

    App *app = new App;
    assert(app);
    if (app != NULL)
    {
        bool result = app->init();
        if (result)
        {
            // Create a state machine for different app states
            // if you create more of them...

            MainState *ms = new MainState;
            if (ms && ms->init())
            {
                app->setAppStateHandler(ms);
                app->run();
            }
            else
                gErrorMessage("Error", "Couldn't initialize MainState.");
            delete ms;
        }
        delete app;
    }

    return 0;
}
